#   vIRCBot, a simple IRC bot
#
#   Copyright (C) 2022 Ferass EL HAFIDI <vitali64pmemail@protonmail.com>
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU Affero General Public License as
#   published by the Free Software Foundation, either version 3 of the
#   License, or (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU Affero General Public License for more details.
#
#   You should have received a copy of the GNU Affero General Public License
#   along with this program.  If not, see <https://www.gnu.org/licenses/>.


# Import Miniirc
import miniirc
from miniirc_extras import *
# Required for messages delay (0.5 secs delay or else its connection 
#                              becomes unstable for some reason)
import time
# Import configs
from bot_config     import *
# Import things required for indexing URLs
import re
from urllib.request import urlopen
from html import unescape # For unescaping html (

# This works only with version >= 1.4.0 of miniirc
assert miniirc.ver >= (1, 4, 0), "vIRCBot requires miniirc >= v1.4.0."

# Create a miniirc 'irc' object
irc = miniirc.IRC(server, serverport, nick, channels, ns_identity=nickserv, debug=True)

print("Welcome to vIRCBot!")

# Define receivedTellMsg
receivedTellMsg = True

# Send messages the convienient way
def ircSendMSG(channel, message):
    time.sleep(0.5)
    irc.send('PRIVMSG', channel, message)

# Help message
def helptext(channel):
    helptext = [
                " )introduce : Introduce myself.",
                " --- Join and leave ---",
                " invite <bot> <channel> : Join a channel.",
                " [%s] )part : Leave this channel." % cmd_part,
                " --- Echo messages ---",
                " [%s] )echo message : Echo a message in this channel." % cmd_echo,
                " [%s] )chanecho message>><pm/chan> : Echo a message in a pm/channel." % cmd_chanecho,
                " [%s] )quackecho : You guessed it!" % cmd_quackecho,
                " [%s] )tell user << message : Tell someone something " % cmd_tell,
                " as soon as they send a message.",
                " --- Channel management ---",
                " [%s] )op <user> : OP a user." % chan_management,
                " [%s] )kick <user> : KICK a user." % chan_management,
                " --- Bot configuration ---",
                " )config <config option> : WIP!",
                " )nick <nick> : Change nick."
               ]

    i=0
    for text in helptext:
        time.sleep(0.5)
        ircSendMSG(channel, helptext[i])
        i=i+1

@miniirc.Handler('PRIVMSG', colon=False)
def help(irc, hostmask, args):
    text = args[-1]
    words = args[-1].split(" ")
    t = text.lower()
    w = [x.lower() for x in words]
    channel = args[0]
    # Support relay bots
    if "[" + words[0][words[0].find("[")+1:words[0].find("]")] + "]" in words[0]:
        if "<" + words[1][words[1].find("<")+1:words[1].find(">")] + ">" in words[1]:
            words.pop(0)
            words.pop(0)

    command = words[0].lower()
    if command.startswith(")help"):
        ircSendMSG(channel, "Hi! My name is %s! I'm powered by vIRCBot, " % irc.nick
                            + "a libre software made by vitali64! "
                            "If you have any issues, you can contact my owner. "
                            "His name is %s." % owner[0]
                            + "If you want to see available commands, say ')commands'. "
                            "The source code is located here: %s." 
                            % sourcecodeurl)
    elif command.startswith(")commands"):
        ircSendMSG(channel, "%s: I've sent a list of commands on a PM." % hostmask[0])
        helptext(hostmask[0])

# Handle invites

@miniirc.Handler('INVITE', colon=False)
def invite(irc, hostmask, args):
    channel = args[-1].split(" ")[0]
    time.sleep(5)
    irc.send('JOIN', channel)
    ircSendMSG(channel, "Hello! I'm a bot! Someone told me to "
                        + "join this channel so I did. "
                        + "If you want me to leave, say ')part'"
                        + " while being op.")

# Here goes your own features.
# For adding one, you can add something like this:
# if command.startswith(")mycommand"): Verifies if your message starts with 
#                                      your command
#    ircSendMSG('PRIVMSG', channel, "my message!") Sends a message
#    irc.send('NICK', "nick_change_time_XD") Changes the bot's nickname
@miniirc.Handler('PRIVMSG', colon=False)
def customFeatures(irc, hostmask, args):
    text = args[-1]
    words = args[-1].split(" ")
    w = [x.lower() for x in words]
    channel = args[0]

    # Support relay bots
    if "[" + words[0][words[0].find("[")+1:words[0].find("]")] + "]" in words[0]:
        if "<" + words[1][words[1].find("<")+1:words[1].find(">")] + ">" in words[1]:
            words.pop(0)
            words.pop(0)

    command = words[0].lower()

    if channel.startswith("#"):
        ops = irc.chans[channel].modes.getset("o")
        isChannel = True
    else:
        isChannel = False
    # Some built-in commands
    if command.startswith(")help"):
        # Help message (since we already have a function 
        # for that, we just say 'pass'.
        pass
    elif command.startswith(")nick") and hostmask[1] == owner[1]:
        ircSendMSG(channel, "Changing nick ...")
        irc.send('NICK', words[1])
    elif command.startswith(")config"):
        # Currently doesn't work at all :^)
        ircSendMSG(channel, "Please note that this command is a WIP!")
    elif command.startswith(")introduce") or command.startswith(nick):
        # Introduce itself
        ircSendMSG(channel, "Hello! I'm a bot. Type )help for more details.")
    elif command.startswith(")join") and cmd_join == "on":
        pass
#        # Join channels
#        ircSendMSG(channel, "Joining %s" % words[1])
#        irc.send('JOIN', words[1])
#        ircSendMSG(words[1], "Hello! I'm a bot! Someone told me to")
#        ircSendMSG(words[1], "join this channel so I did.")
#        ircSendMSG(words[1], "If you want me to leave, say ')part'.")
    elif command.startswith(")echo") and cmd_echo == "on":
        # Echo messages
        ircSendMSG(channel, "%s" % args[-1].split("//////")[0].replace(")echo", "Echo:"))
    elif command.startswith(")chanecho") and cmd_chanecho == "on":
        # Echo messages in other rooms
        if isChannel == True:
            ircSendMSG(args[-1].split(">>")[1], "%s" % 
                       args[-1].split(">>")[0].replace(")chanecho",
                                                       "Message from %s: "
                                                       % hostmask[0]))
    elif command.startswith(")mysteriouschanecho") and cmd_chanecho == "on":
        # Echo messages in other rooms
        if isChannel == True:
            ircSendMSG(args[-1].split(">>")[1], "%s" % 
                       args[-1].split(">>")[0].replace(")mysteriouschanecho",""))

    elif command.startswith(")part") and cmd_part == "on":
        # Leave the current channel
        if isChannel != True:
            # If it's a PM, don't do anything
            ircSendMSG(channel, "You need to be in a channel to do that.")
        elif hostmask[0] in ops:
            # If the one running the command is OP, leave
            ircSendMSG(channel, "Goodbye!")
            irc.send('PART', channel, "Requested by %s" % hostmask[0])
        else:
            # Else, complain about them not being op
            ircSendMSG(channel, "You do not have permission to do this.")
    elif command.startswith(")kick") and chan_management == "on":
        # Kick a user
        if isChannel != True:
            # If the bot isn't on a channel, don't do anything
            ircSendMSG(channel, "You need to be in a channel to do that.")
        elif hostmask[0] in ops:
            # If the one running the command is OP, kick the specified user
            ircSendMSG(channel, "Kicking %s" % words[1])
            irc.send('KICK', channel, words[1],
            "You annoyed everyone here, please sit down and relax for a second")
        else:
            # Else, complain about the one running the command that they're not OP
            ircSendMSG(channel, "You do not have permission to do this.")
    elif command.startswith(")op") and chan_management == "on":
        # OP a user
        if isChannel != True:
            # It's dumb to op someone in a PM
            ircSendMSG(channel, "You need to be in a channel to do that.")
        elif hostmask[0] in ops:
            # If the one running the command is themselves OP, do it
            ircSendMSG(channel, "Giving OP to %s" % words[1])
            irc.send('MODE', 'o', words[1])
        else:
            # Else, complain
            ircSendMSG(channel, "You do not have permission to do this.")
    elif command.startswith(")quackecho") and cmd_quackecho == "on":
        # QUACK?!Echo XD
        ircSendMSG(channel, "QUACK?!ECHO: º·.º ·.º· º·..º·. \\_o< %s?!" % words[1])
    elif "http://" in text or "https://" in text:
        # The way of doing it may change in the future
        foundURL = False
        i=0
        while foundURL == False:
            if words[i].startswith("http"):
                url = words[i]
                foundURL = True
                html_string = str(urlopen(url).read())
                match = re.search('<title>(.*?)</title>', html_string)
                title = unescape(match.group(1)) if match else False
                if title != False: # If a title is detected, print it.
                    # Also unescape it (')amp;' -> ')')
                    ircSendMSG(channel, "Title: %s" % title)
            else:
                i=i+1
    elif command.startswith(")tell") and cmd_tell == "on":
        global tellMessage     # Global variables that are accessible everywhere
        global tellUser        # Needed for the handleTellMessages function to 
        global receivedTellMsg # Work.
        global sentByUser
        if hostmask[0] == words[1]:
            ircSendMSG(channel, "%s: You can tell yourself that." % hostmask[0])
        elif words[1] == irc.nick:
            ircSendMSG(channel, 
                       "%s: I'm already there, you can tell me everything!" % hostmask[0])
        elif "<<" not in args[-1]:
            ircSendMSG(channel, 
                       "%s: Usage: )tell user << message" % hostmask[0])
        else:
            tellMessage     = args[-1].split("<<")[-1]
            tellUser        = words[1]
            receivedTellMsg = False
            sentByUser      = hostmask[0]
            ircSendMSG(channel, "%s: I'll tell %s that when they're around." % ( hostmask[0], tellUser ))

# Tell people about things (?)
@miniirc.Handler('PRIVMSG', colon=False)
def handleTellMessages(irc, hostmask, args):
    global receivedTellMsg
    global tellUser
    global tellMessage
    global sentByUser
    channel = args[0]
    if receivedTellMsg == False and hostmask[0] == tellUser:
            receivedTellMsg = True
            ircSendMSG(channel, "%s: %s wanted to tell you this:%s" 
                       % ( hostmask[0], sentByUser, tellMessage ))

# If a user connects, welcome them
@miniirc.Handler('JOIN', colon=False)
def welcomeUser(irc, hostmask, args):
    channel = args[0]
    if hostmask[0] != irc.nick and welcome_user == "on":
        # This checks if the one who joined isn't actually the bot and also if 
        # the option to greet the user is enabled.
        ircSendMSG(channel, "Hello %s, welcome to this channel!" % hostmask[0])
        ircSendMSG(channel, introduction)

# If the script is being run directly, do this:
if __name__ == '__main__':
    # Usual IRC shenanigans
    irc.require("users")
    irc.require("chans")
    irc.connect()
    irc.wait_until_disconnected()
